﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using OWLServer.owl.tipos;

namespace OWLServer.owl.utilidades
{
    /// <summary>
    /// Clase que contiene métodos para aplicar las inferencias que se producen por las propiedades.
    /// </summary>
    class UtilidadesInferencias
    {
        /// <summary>
        /// Aplica a los individuos de la ontología las inferencias que se producen a partir de los tipos de propiedades que tienen.
        /// </summary>
        /// <param name="ontologia">Ontolología a la que se va aplicar las inferencias</param>
        public static void aplicarInferencias(Ontologia ontologia)
        {
            aplicarInferenciasPropiedadesFuncionales(ontologia);
            aplicarInferenciasPropiedadesInversasFuncionales(ontologia);
            aplicarInferenciasPropiedadesSimetrica(ontologia);
            aplicarInferenciasPropiedadesTransitivas(ontologia);
        }

        /// <summary>
        /// Aplica a los individuos de la ontología las inferencias que se producen a partir de las propiedades de tipo funcional.
        /// P(x,y) y P(x,z) => y=z
        /// </summary>
        /// <param name="ontologia">Ontolología a la que se va aplicar las inferencias</param>
        private static void aplicarInferenciasPropiedadesFuncionales(Ontologia ontolologia)
        {
            foreach (Individuo individuo in ontolologia.Individuos)
            {
                // Analizamos las propiedades de tipo objeto
                for (int i = 0; i < individuo.PropiedadesObjetos.Count; i++)
                {
                    if (individuo.PropiedadesObjetos[i].PropiedadObjeto.Tipos.Contains(TipoPropiedad.FUNCIONAL))
                    {
                        for (int j = i + 1; j < individuo.PropiedadesObjetos.Count; j++)
                        {
                            if (individuo.PropiedadesObjetos[i].PropiedadObjeto.Equals(individuo.PropiedadesObjetos[j].PropiedadObjeto))
                            {
                                crearIgualdadIndividuos(individuo.PropiedadesObjetos[i].Individuo, individuo.PropiedadesObjetos[j].Individuo);
                            }
                        }
                    }
                }
            }
        }

        /// <summary>
        /// Aplica a los individuos de la ontología las inferencias que se producen a partir de las propiedades de tipo inversa funcional.
        /// P(y,x) y P(z,x) => y=z
        /// </summary>
        /// <param name="ontologia">Ontolología a la que se va aplicar las inferencias</param>
        private static void aplicarInferenciasPropiedadesInversasFuncionales(Ontologia ontolologia)
        {
            for (int i = 0; i < ontolologia.Individuos.Count; i++)
            {
                foreach (PropiedadObjetoIndividuo propiedadInv1 in ontolologia.Individuos[i].PropiedadesObjetos)
                {
                    if (propiedadInv1.PropiedadObjeto.Tipos.Contains(TipoPropiedad.INVERSA_FUNCIONAL))
                    {
                        for (int j = i + 1; j < ontolologia.Individuos.Count; j++)
                        {
                            foreach (PropiedadObjetoIndividuo propiedadInv2 in ontolologia.Individuos[j].PropiedadesObjetos)
                            {
                                if (propiedadInv1.PropiedadObjeto.Equals(propiedadInv2.PropiedadObjeto) && propiedadInv1.Individuo.Equals(propiedadInv2.Individuo))
                                {
                                    crearIgualdadIndividuos(ontolologia.Individuos[i], ontolologia.Individuos[j]);
                                }
                            }
                        }
                    }
                }
            }
        }

        /// <summary>
        /// Aplica a los individuos de la ontología las inferencias que se producen a partir de las propiedades de tipo transitiva.
        /// P(x,y) y P(y,z) => P(x,z)
        /// </summary>
        /// <param name="ontologia">Ontolología a la que se va aplicar las inferencias</param>
        private static void aplicarInferenciasPropiedadesTransitivas(Ontologia ontolologia)
        {
            PropiedadObjetoIndividuo propiedadInv1;
            foreach (Individuo individuo in ontolologia.Individuos)
            {
                for (int i = 0; i < individuo.PropiedadesObjetos.Count; i++)
                {
                    propiedadInv1 = individuo.PropiedadesObjetos[i];
                    if (propiedadInv1.PropiedadObjeto.Tipos.Contains(TipoPropiedad.TRANSITIVA))
                    {
                        foreach (PropiedadObjetoIndividuo propiedadInv2 in propiedadInv1.Individuo.PropiedadesObjetos)
                        {
                            if (propiedadInv2.PropiedadObjeto.Equals(propiedadInv1.PropiedadObjeto))
                            {
                                crearPropiedad(propiedadInv1.PropiedadObjeto, individuo, propiedadInv2.Individuo);
                            }

                        }
                    }
                }
            }
        }

        /// <summary>
        /// Aplica a los individuos de la ontología las inferencias que se producen a partir de las propiedades de tipo simetrica.
        /// P(x,y) iff P(y,x). Con que lo tenga x se le añade a y.
        /// </summary>
        /// <param name="ontologia">Ontolología a la que se va aplicar las inferencias</param>
        private static void aplicarInferenciasPropiedadesSimetrica(Ontologia ontolologia)
        {
            PropiedadObjetoIndividuo propiedadInv1;
            foreach (Individuo individuo in ontolologia.Individuos)
            {
                for (int i = 0; i < individuo.PropiedadesObjetos.Count; i++)
                {
                    propiedadInv1 = individuo.PropiedadesObjetos[i];
                    if (propiedadInv1.PropiedadObjeto.Tipos.Contains(TipoPropiedad.SIMETRICA))
                    {
                        crearPropiedad(propiedadInv1.PropiedadObjeto, propiedadInv1.Individuo, individuo);
                    }
                }
            }
        }

        /// <summary>
        /// Hace igual el individuo1 con el individuo2 y bicebersa
        /// </summary>
        /// <param name="individuo1">Individuo 1</param>
        /// <param name="individuo2">individuo 2</param>
        private static void crearIgualdadIndividuos(Individuo individuo1, Individuo individuo2)
        {
            if (!individuo1.Iguales.Contains(individuo2) && !individuo1.Equals(individuo2))
            {
                individuo1.Iguales.Add(individuo2);
                crearIgualdadIndividuos(individuo2, individuo1);
            }
        }

        /// <summary>
        /// Crea una propiedad, de tipo objeto, en el individuo 1 que lo vincula con el individuo 2
        /// </summary>
        /// <param name="propiedad">Propiedad a crear.</param>
        /// <param name="individuo1">Individuo al que se le va a crear la propiedad.</param>
        /// <param name="individuo2">Individuo con el que se va a vincular la propiedad.</param>
        private static void crearPropiedad(PropiedadObjeto propiedad, Individuo individuo1, Individuo individuo2)
        {
            PropiedadObjetoIndividuo propiedadIndividuo = new PropiedadObjetoIndividuo(individuo2, propiedad);
            if (!individuo1.PropiedadesObjetos.Contains(propiedadIndividuo))
            {
                individuo1.PropiedadesObjetos.Add(propiedadIndividuo);
            }
        }
    }
}
